#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#include "quadrdefs.h"


/** Array mapping function names to definitions. */
FnDef fns[] = {
	{ "sin", "sinFn" },
	{ "wikipedia", "wikipediaFn" },
};

/** # of entries in fns[] array. */
int nFns = sizeof(fns)/sizeof(fns[0]);


/* This will check if the given function is available*/
int isFunAvailable (char *strPtr){
	int i =0;
	for (i = 0; i < nFns; i++){
		if (strcmp (strPtr, fns[i].name) == 0)
			return i;
	}
	return -1;
}


/* Pass 0/1 as 2nd argument to checkNum funtion to check if it is double or not */
int checkNum (char *strPtr, int isDouble){
	int i = 0;
	int flag = 0;

	if (strPtr[0] == '.'){
		return 1;
	}

	for (i = 0; i < strlen (strPtr); i++){
		if (strPtr[i] == '.'){
			if (flag == 0)
				flag = 1;
			else
				return 1;
		}
	}

	for (i = 0; i < strlen (strPtr); i ++){
		if (isDouble == 1){

			if (!isdigit((int)strPtr[i]) && strPtr[i] != 'e' && strPtr[i] != 'E' && strPtr[i] != '+' && strPtr[i] != '.' &&
					strPtr[i] != '-'){
				return 1;
			}
		}
		else{
			if (!isdigit((int)strPtr[i])){
				return 1;
			}

		}
		/* Check for same operator does not occur. eg. 12++1, 12-+1, etc */
		if (strPtr[i] == '+' || strPtr[i] == '.' || strPtr[i] == '-'){
			if (!isdigit ((int)strPtr[i+1] )) {
				return 1;
			}
			if ( i != 0 && strPtr[i - 1] == ' ' && strPtr[i-1] == 'e'){
				return 1;
			}
		}
		if (strPtr[i] == 'e' || strPtr[i] == 'E'){
			if (!isdigit((int)strPtr[i+1]) && strlen (strPtr) <= i + 1){
				return 1;
			}
		}
	}
	return 0;
}


Params validate_input(char *line){

	Params prm;

	char *strPtr = NULL, *pEnd = NULL;
	int i = 0, flag = 1, fnNo = 0;

	strPtr = strtok (line, " \n");

	if(strcmp(strPtr, "quit") == 0){
		prm.error = 1;
		return prm;
	}

	while (strPtr != NULL){

		if ( i == 0){
			//Check if the funtion is available in fns[]
			if ( (fnNo = isFunAvailable (strPtr)) != -1){
				prm.fnName = (char *)malloc (sizeof (char) * strlen (fns[fnNo].f));
				strcpy (prm.fnName, fns[fnNo].f);
				printf("Function : %s\n",fns[fnNo].f);
			}
			else{
				fprintf (stderr, "\nERROR: Function \"%s\" is not defined\n", strPtr);
				flag = 1;
				prm.error = -1;
				return prm;
			}
		}

		if ( i == 1){
			/* Pass 1 as 2nd argument to checkNum funtion if the entered value is double */
			if (checkNum(strPtr, 1) == 0){
				prm.lo_bound = strtod (strPtr, &pEnd);
			}
			else{
				fprintf (stderr, "\nERROR: Invalid LO_BOUND value : %s\n", strPtr);
				flag = 1;
				prm.error = -1;
				return prm;
			}
		}

		if ( i == 2){
			if (checkNum (strPtr, 1) == 0){
				prm.hi_bound = strtod (strPtr, &pEnd);
			}
			else{
				fprintf (stderr, "\nERROR: Invalid HI_BOUND value : %s\n", strPtr);
				flag = 1;
				prm.error = -1;
				return prm;
			}
		}

		if ( i == 3){
			/* Pass 0 as 2nd argument to checkNum funtion if the entered value is not double */
			if (checkNum (strPtr, 0) == 0){
				prm.n_intervals = atoi (strPtr);
			}
			else{
				fprintf (stderr, "\nERROR: Invalid N_INTERVALS : %s\n", strPtr);
				flag = 1;
				prm.error = -1;
				return prm;
			}
		}

		if ( i >= 4){
			fprintf (stderr, "\nERROR: Illegal input %s\n", strPtr);
			flag = 1;
			prm.error = -1;
			return prm;
		}

		i++;
		/* Read next argument from line by using strtok */
		strPtr = strtok (NULL, " \n");
	}

	i--;
	if (i != 3){
		fprintf (stderr, "\nERROR: Insufficient values provided\n");
		prm.error = -1;
		return prm;
	}

	return prm;

}

int main (int argc, char *argv[]){

	char line[BUFSIZE] = "";
	char rFile[BUFSIZE] = "";
	char wFile[BUFSIZE] = "";
	int fd, rdFD, wrFD, serverAck, readCnt;
	Params prm;
	double result;
	pid_t clientPID;

	/*line = (char *)malloc (sizeof (char));
	  if (!line){
	  fprintf (stderr, "Not enough memory\n");
	  exit(1);
	  }*/


	if (argc != 2) {
		fprintf(stderr, "usage: %s DIR\n", argv[0]); exit(1);
	}

	//printf ("Dir : %s\n",argv[1]);

	////////////
	/////////////////////////////////////////////

	chdir (argv[1]);

	//printf ("Line : %s %g %g %d\n",prm.fnName, prm.lo_bound, prm.hi_bound, prm.n_intervals);

	fd = open (FIFO_NAME, O_RDWR);
	if (fd == -1){
		fprintf (stderr, "Can't open FIFO for I/O operation\n"); 
		exit (1);
	}

	clientPID = (long)getpid();
	sprintf (line, "%ld\n",clientPID);

	if (write (fd, line, strlen (line)) == -1){
		fprintf (stderr, "Write failed on Server FIFO\n");
		close (fd);
		exit (1);
	}

	if (read (fd, (void *)&serverAck, sizeof (serverAck)) == -1){
		fprintf (stderr, "read failed on FIFO\n");
		close (fd);
		exit (1);
	}

	//fprintf (stderr, "CLIENT:%ld Received ACK %d from server\n", clientPID, serverAck); 
	if (serverAck == 0){
		fprintf (stderr, "ERROR: Can't create communication channel between server.\n");
		close(fd);
		exit(1);
	}

	sprintf (rFile,"%ld_rd",clientPID);
	sprintf (wFile,"%ld_wr",clientPID);

	rdFD = open (rFile, O_RDWR);
	if (fd == -1){
		fprintf (stderr, "Can't open Read FIFO for I/O operation\n");
		close (fd);
		exit (1);
	}

	//fprintf (stderr, "CLIENT:%ld rFile open done\n",clientPID); 

	wrFD = open (wFile, O_WRONLY);
	if (fd == -1){
		fprintf (stderr, "Can't open write FIFO for I/O operation\n");
		close (fd);
		exit (1);
	}

	//fprintf (stderr, "CLIENT:%ld wFile open done\n",clientPID); 
	while(1){
		fprintf (stdout, "\nFN LO_BOUND HI_BOUND N_INTERVALS\n");
		fprintf (stdout, "(Type 'quit' for closing.)\n");

		line[0] = '\0';
		if ((readCnt = read (STDIN_FILENO,line, BUFSIZE)) == -1){
			fprintf (stderr, "ERROR: Unable to read line\n");
			break;
		}

		//printf ("\n\nLine : %s____ readCnt %d\n\n",line,readCnt);
		if (readCnt == 0){
			continue;
		}
		prm = validate_input (line);
		if (prm.error == -1){
			fprintf (stderr, "Invalid Input\n");
			continue;
		}

		if (prm.error == 1){
			fprintf (stdout,"Cleaning client specific FIFO information.\n");
			break;
		}

		sprintf (line, "%s %g %g %d\n",prm.fnName, prm.lo_bound, prm.hi_bound, prm.n_intervals);
		if (write (wrFD, line, sizeof (line)) == -1){
			fprintf (stderr, "Write failed on Client FIFO\n");
			break;
		}

		if ((readCnt = read (rdFD, (void *)&result, sizeof (result))) == -1){
			fprintf (stderr, "read failed on Client FIFO: readCnt : %d \n",readCnt);
			break;
		}

		if (result == -1){
			fprintf (stderr, "Can't compute the result.Exiting \n");
			break;
		}
		fprintf (stdout,"CLIENT:%ld Received Trapezoidal result for %s function : %.6g\n", (long)getpid(), prm.fnName, result);

		/* Free the line before returning. Free it only if it has data */
		/*if (line)
		  free (line);
		 */
	}
	close(fd);
	close (rdFD);
	close (wrFD);

	unlink(rFile);
	unlink(wFile);
}
